package eventplanner.ejb;

import eventplanner.ejb.dao.FeedDao;
import eventplanner.ejb.dao.TokenDao;
import eventplanner.ejb.exceptions.IllegalArgumentFoundException;
import eventplanner.ejb.exceptions.NoSuchEntityFoundException;
import eventplanner.ejb.exceptions.UnauthorizedException;
import eventplanner.ejb.interceptors.LoginSecurityInterceptor;
import eventplanner.entities.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ejb.EJB;
import javax.ejb.Stateful;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.interceptor.ExcludeClassInterceptors;
import javax.interceptor.Interceptors;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.util.List;

@Stateful
@SessionScoped
@Interceptors(LoginSecurityInterceptor.class)
public class ServiceFacade {

	private static final Logger LOG = LoggerFactory.getLogger(ServiceFacade.class);

	@EJB
	private FeedDao feedDao;

	@EJB
	private TokenDao tokenDao;

	@PersistenceContext
	private EntityManager em;

	@Inject
	private Session session;

	@ExcludeClassInterceptors
	public void login(String username, String password) {
		session.setLoggedIn(true);
		session.setUsername(username);
		TypedQuery<User> query = em.createQuery("select user from User user where user.name = ?1", User.class);
		query.setParameter(1, "Wouter");
		session.setUser(query.getSingleResult());
	}

	@ExcludeClassInterceptors
	public void logout() {
		session.setLoggedIn(false);
		session.end();
	}

	@ExcludeClassInterceptors
	public boolean isLoggedIn() {
		LOG.info("Islogged in '{}' user '{}'", session.isLoggedIn(), session.getUser());

		return session.isLoggedIn();
	}

	@ExcludeClassInterceptors
	public List getCalendars() {
		if (session.isLoggedIn()) {
			return feedDao.getAllIdsAndNamesOfAllFeeds();
		} else {
			return feedDao.getAllIdsAndNamesOfPublicFeeds();
		}
	}

	public String getAuthLevel() {
		String username = session.getUsername();
		if (username.endsWith("@student.hu.nl")) {
			return "student";
		} else if (username.endsWith("@hu.nl")) {
			return "medewerker";
		} else {
			return "onbekend";
		}
	}

	@ExcludeClassInterceptors
	public String getInfo() {
		return session.getUsername();
	}

	public Feed getCalendar(long calendarId) {
		TypedQuery<Feed> query = em.createQuery("select f from Feed f left join fetch f.events where f.id = ?1", Feed.class);
		query.setParameter(1, calendarId);
		Feed feed = query.getSingleResult();

		if (session.isLoggedIn() || feed.isPublic()) {
			return feed;
		}
		throw new UnauthorizedException();
	}

	public List<String> getUserTokens() {
		TypedQuery<String> query = em.createQuery("select t.tokenString from Token t where t.user = ?1", String.class);
		query.setParameter(1, session.getUser());
		return query.getResultList();
	}

	public String createNewToken() {
		Token token = Token.createToken();
		token.setUser(session.getUser());
		em.persist(token);

		LOG.info("New token created: {}", token.toString());

		return token.getTokenString();
	}

	public boolean addFeedToToken(long tokenId, long feedId) {
		Token token = tokenDao.find(tokenId);
		Feed feed = em.find(Feed.class, feedId);

		checkForNull(tokenId, token, Token.class);
		checkForNull(feedId, feed, Feed.class);

		boolean isSuccessful = token.getFeeds().add(feed);
		LOG.info("Attempt add feed (id:{}) from token (id:{}) result {}", new Object[]{feedId, tokenId, isSuccessful});
		return isSuccessful;
	}

	public boolean delFeedFromToken(long tokenId, long feedId) {
		Token token = tokenDao.find(tokenId);
		Feed feed = em.find(Feed.class, feedId);

		checkForNull(tokenId, token, Token.class);
		checkForNull(feedId, feed, Feed.class);

		boolean isSuccessful = token.getFeeds().remove(feed);
		LOG.info("Attempt to remove feed (id:{}) from token (id:{}) result {}", new Object[]{feedId, tokenId, isSuccessful});
		return isSuccessful;
	}

	public boolean addTagToToken(long tokenId, String tag) {
		if (tag == null || tag.trim().isEmpty()) {
			throw new IllegalArgumentFoundException(tag);
		}
		Token token = tokenDao.find(tokenId);
		checkForNull(tokenId, token, Token.class);
		boolean isSuccessful = token.getTags().add(tag.trim());
		LOG.info("Attempt to add tag '{}' to token (id:{}), result {}", new Object[]{tag, tokenId, isSuccessful});
		return isSuccessful;
	}

	public boolean delTagFromToken(long tokenId, String tag) {
		Token token = tokenDao.find(tokenId);
		checkForNull(tokenId, token, Token.class);
		boolean isSuccessful = token.getTags().remove(tag);
		LOG.info("Attempt to remove tag '{}' from token (id:{}), result {}", new Object[]{tag, tokenId, isSuccessful});
		return isSuccessful;
	}

	@ExcludeClassInterceptors
	public Event getEvent(long id) {
		Event event = em.find(Event.class, id);
		checkForNull(id, event, Event.class);

		TypedQuery<Feed> query = em.createQuery("select f from Feed f join f.events e where e.id = ?1", Feed.class);
		query.setParameter(1, id);
		Feed f = query.getSingleResult();
		if (f == null) {
			throw new NoSuchEntityFoundException(Feed.class, "-1");
		}

		if (session.isLoggedIn() || f.isPublic()) {
			return event;
		} else {
			throw new UnauthorizedException();
		}
	}

	private void checkForNull(Object id, Object entity, Class<?> type) {
		if (entity == null) {
			throw new NoSuchEntityFoundException(type, id);
		}
	}

	@ExcludeClassInterceptors
	public List<Event> getTodaysEvents() {
		TypedQuery<Event> query = em.createQuery(
				"select e from Feed f inner join f.events e where f.isPublic = true order by e.start asc", Event.class);
		query.setMaxResults(15);

		return query.getResultList();

	}

	@ExcludeClassInterceptors
	public Object getToken(String tokenString) {                                                                             //, t.tags, t.organizers, t.targets
		Query query = em.createQuery("select t.id, t.name, t.tokenString from Token t where t.tokenString = ?1");
		query.setParameter(1, tokenString);
		return query.getResultList();
	}

	@ExcludeClassInterceptors
	public Object getTagsFromToken(long id) {
		TypedQuery<String> query = em.createQuery("select tag from Token t left join t.tags tag where t.id = ?1", String.class);
		query.setParameter(1, id);
		return query.getResultList();
	}

	@ExcludeClassInterceptors
	public Object getTargetFromToken(long id) {
		TypedQuery<String> query = em.createQuery("select target from Token t left join t.targets target where t.id = ?1", String.class);
		query.setParameter(1, id);
		return query.getResultList();
	}

	@ExcludeClassInterceptors
	public Object getOrganizerFromToken(long id) {
		TypedQuery<String> query = em.createQuery("select organizer from Token t left join t.organizers organizer where t.id = ?1", String.class);
		query.setParameter(1, id);
		return query.getResultList();
	}


	@ExcludeClassInterceptors
	public List<Doelgroep> getAllGroups() {
		return em.createQuery("select g from Doelgroep g", Doelgroep.class).getResultList();
	}

	public boolean addTargetToToken(long tokenId, String target) {
		if (target == null || target.trim().isEmpty()) {
			throw new IllegalArgumentFoundException("");
		}
		Token token = em.find(Token.class, tokenId);

		checkForNull(tokenId, token, Token.class);


		return token.getTargets().add(target);

	}

	public boolean delTargetToToken(long tokenId, String target) {
		if (target == null || target.trim().isEmpty()) {
			throw new IllegalArgumentFoundException("");
		}
		Token token = em.find(Token.class, tokenId);

		checkForNull(tokenId, token, Token.class);


		return token.getTargets().remove(target);
	}

	public boolean addOrganizerToToken(long tokenId, String target) {
		if (target == null || target.trim().isEmpty()) {
			throw new IllegalArgumentFoundException("");
		}
		Token token = em.find(Token.class, tokenId);

		checkForNull(tokenId, token, Token.class);
		return token.getOrganizers().add(target);

	}

	public boolean delOrganizerToToken(long tokenId, String target) {
		if (target == null || target.trim().isEmpty()) {
			throw new IllegalArgumentFoundException("");
		}
		Token token = em.find(Token.class, tokenId);

		checkForNull(tokenId, token, Token.class);


		return token.getOrganizers().remove(target);
	}


	public Object getFeeds(long id) {
		Query query = em.createQuery("select f.id, f.name from Token t join t.feeds f where t.id = ?1");
		query.setParameter(1, id);
		return query.getResultList();
	}
}
